package com.srit.recipes.service.impl;

import com.srit.recipes.common.parent.DaoInject;
import com.srit.recipes.common.response.JpaPage;
import com.srit.recipes.entity.*;
import com.srit.recipes.service.InventoryService;
import com.srit.recipes.vo.InventoryCompare;
import org.apache.poi.hssf.record.DVALRecord;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.ExampleMatcher;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class InventoryServiceImpl extends DaoInject implements InventoryService {
    @Override
    public Object inventoryList(JpaPage jpaPage, InventoryInfo inventoryInfo) {
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());

        Example<InventoryInfo> example = Example.of(inventoryInfo, exampleMatcher);


        return JpaPage.build(inventoryDao.findAll(example, JpaPage.getPageable(jpaPage)));
    }

    @Override
    public Object inventoryHis(JpaPage jpaPage, InventoryHis inventoryHis) {

        Specification<InventoryHis> specification = (root, query, criteriaBuilder) -> {

            List<Predicate> predicates = new ArrayList<>();
            if (StringUtils.hasLength(inventoryHis.getName())) {
                predicates.add(query.where(criteriaBuilder.like(root.get("name"), "%" + inventoryHis.getName() + "%")).getRestriction());
            }

            if (inventoryHis.getCreateTime() != null) {
                predicates.add(query.where(criteriaBuilder.between(root.get("createTime"), inventoryHis.getCreateTime(), inventoryHis.getEndTime())).getRestriction());
            }

            Predicate type = query.where(criteriaBuilder.equal(root.get("type"), inventoryHis.getType())).getRestriction();

            return predicates.stream().reduce(type, criteriaBuilder::and);
        };

        return JpaPage.build(inventoryHisDao.findAll(specification, JpaPage.getPageable(jpaPage, Sort.by("createTime").descending())));
    }

    @Override
    public Object inventoryUpdateIn(InventoryHis inventoryHis) {


        if (inventoryHis.getPrice() == null) {
            ScPriceInfo byId = scPriceDao.getById(inventoryHis.getName());
            if (byId != null) {
                inventoryHis.setPrice(byId.getPrice() * inventoryHis.getWight() / 1000);
            } else {
                inventoryHis.setPrice(0D);
            }
        }
        InventoryHis save = inventoryHisDao.save(inventoryHis);
        int type = save.getType();
        if (type == InventoryHis.IN) {
            InventoryInfo inventoryInfo = new InventoryInfo();
            inventoryInfo.setBatch(save.getBatch());
            inventoryInfo.setName(save.getName());
            inventoryInfo.setUnit(save.getUnit());
            inventoryInfo.setExpiration(save.getExpiration());
            inventoryInfo.setWight(save.getWight());
            inventoryInfo.setDistributor(save.getDistributor());
            inventoryInfo.setPrice(inventoryHis.getPrice());
            inventoryDao.save(inventoryInfo);
        }
        return save;
    }

    @Override
    public Object inventoryUpdateOut(int id, int wight) {
        InventoryInfo inventoryInfo = inventoryDao.getById(id);

        if (inventoryInfo.getWight() == wight) {
            inventoryDao.deleteById(id);
        } else {
            inventoryInfo.setWight(inventoryInfo.getWight() - wight);
            inventoryDao.save(inventoryInfo);
        }
        InventoryHis inventoryHis = new InventoryHis();
        inventoryHis.setBatch(inventoryInfo.getBatch());
        inventoryHis.setName(inventoryInfo.getName());
        inventoryHis.setUnit(inventoryInfo.getUnit());
        inventoryHis.setExpiration(inventoryInfo.getExpiration());
        inventoryHis.setWight((long) wight);
        inventoryHis.setDistributor(inventoryInfo.getDistributor());
        inventoryHis.setType(InventoryHis.OUT);
        inventoryHis.setPrice(inventoryHis.getPrice() == null ? 0 : inventoryHis.getPrice() * wight / inventoryInfo.getWight());
        inventoryHisDao.save(inventoryHis);
        return inventoryInfo;
    }

    @Override
    public Object inventoryExceed(JpaPage jpaPage, int type) {

        if (type == 0) {
            // 即将过期
            return JpaPage.build(inventoryDao.findAllByExpirationBetween(new Date(System.currentTimeMillis()), new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(7)), JpaPage.getPageable(jpaPage, Sort.by("expiration"))));
        } else {
            return JpaPage.build(inventoryDao.findAllByExpirationBefore(new Date(System.currentTimeMillis()), JpaPage.getPageable(jpaPage, Sort.by("expiration"))));
        }
    }

    @Override
    public Object equipmentList(JpaPage jpaPage, EquipmentInfo equipmentInfo) {
        ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("name", ExampleMatcher.GenericPropertyMatchers.contains());

        Example<EquipmentInfo> example = Example.of(equipmentInfo, exampleMatcher);
        return JpaPage.build(equipmentDao.findAll(example, JpaPage.getPageable(jpaPage)));
    }

    @Override
    public Object equipmentAddOrUpdate(EquipmentInfo equipmentInfo) {
        return equipmentDao.save(equipmentInfo);
    }

    @Override
    public Object deleteEquipment(int id) {
        equipmentDao.deleteById(id);
        return true;
    }

    @Override
    public Object inventoryCompare(String date) {
        LocalDate localDate = LocalDate.parse(date, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
        List<InventoryCompare> inventoryCompares = new ArrayList<>();
        List<InventoryHis> his = inventoryHisDao.findAllByTypeAndCreateTimeBetween(InventoryHis.IN, getStartTime(localDate), getEndTime(localDate));
        DayOfWeek dayOfWeek = localDate.getDayOfWeek();
        List<ProcurementDetailInfo> allByWeek = procurementDetailDao.findAllByWeek(dayOfWeek.getValue() - 1);

        Map<String, List<InventoryHis>> hisMap = his.stream().collect(Collectors.groupingBy(InventoryHis::getName));

        for (ProcurementDetailInfo procurementDetailInfo : allByWeek) {

            List<InventoryHis> inventoryHis = hisMap.get(procurementDetailInfo.getName());
            long in = 0;
            if (inventoryHis != null) {
                in = inventoryHis.stream().map(InventoryHis::getWight).reduce(0L, Long::sum);
            }
            if (in != procurementDetailInfo.getWight()) {
                InventoryCompare inventoryCompare = new InventoryCompare();
                inventoryCompare.setName(procurementDetailInfo.getName());
                inventoryCompare.setDiffer(procurementDetailInfo.getWight() - in);
                inventoryCompares.add(inventoryCompare);
            }
        }

        return inventoryCompares;
    }

    private static Date getStartTime(LocalDate localDate) {
        Calendar todayStart = Calendar.getInstance();
        todayStart.set(localDate.getYear(), localDate.getMonthValue() - 1, localDate.getDayOfMonth(), 0, 0, 0);
        todayStart.set(Calendar.MILLISECOND, 0);
        return todayStart.getTime();
    }

    private static Date getEndTime(LocalDate localDate) {
        Calendar todayEnd = Calendar.getInstance();
        todayEnd.set(localDate.getYear(), localDate.getMonthValue() - 1, localDate.getDayOfMonth(), 23, 59, 59);
        todayEnd.set(Calendar.MILLISECOND, 999);
        return todayEnd.getTime();
    }
}
